package com.lulin.mall.order.service.impl;

import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.lulin.common.constant.OrderConstant;
import com.lulin.common.exception.NoStockExecption;
import com.lulin.common.utils.R;
import com.lulin.common.vo.MemberVo;
import com.lulin.mall.order.dto.OrderCreateDTO;
import com.lulin.mall.order.entity.OrderItemEntity;
import com.lulin.mall.order.fegin.CartFeignService;
import com.lulin.mall.order.fegin.MemberFeignService;
import com.lulin.mall.order.fegin.ProductFeignService;
import com.lulin.mall.order.fegin.WareFeignService;
import com.lulin.mall.order.interceptor.AuthInterceptor;
import com.lulin.mall.order.service.OrderItemService;
import com.lulin.mall.order.utils.OrderMsgProducer;
import com.lulin.mall.order.vo.*;
import io.seata.spring.annotation.GlobalTransactional;
import org.checkerframework.checker.units.qual.A;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.lulin.common.utils.PageUtils;
import com.lulin.common.utils.Query;

import com.lulin.mall.order.dao.OrderDao;
import com.lulin.mall.order.entity.OrderEntity;
import com.lulin.mall.order.service.OrderService;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

@Service("orderService")
public class OrderServiceImpl extends ServiceImpl<OrderDao, OrderEntity> implements OrderService {

    @Autowired
    MemberFeignService memberFeignService;

    @Autowired
    CartFeignService cartFeignService;

    @Autowired
    ThreadPoolExecutor poolExecutor;

    @Autowired
    RedisTemplate redisTemplate;

    @Autowired
    ProductFeignService productFeignService;

    @Autowired
    OrderService orderService;

    @Autowired
    OrderItemService orderItemService;
    @Autowired
    WareFeignService wareFeignService;

    @Autowired
    OrderMsgProducer orderMsgProducer;

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<OrderEntity> page = this.page(
                new Query<OrderEntity>().getPage(params),
                new QueryWrapper<OrderEntity>()
        );

        return new PageUtils(page);
    }

    /**
     * 获取订单结算确认页需要的相关信息
     */
    @Override
    public OrderConfirmVo confirmOrder() {
        OrderConfirmVo confirmVo = new OrderConfirmVo();
        MemberVo memberVo = (MemberVo) AuthInterceptor.threadLocal.get();
        // 获取到 RequestContextHolder 的相关信息
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();

        CompletableFuture<Void> future1 = CompletableFuture.runAsync(() -> {
            //同步主线程的requestAttributes
            RequestContextHolder.setRequestAttributes(requestAttributes);

            //1、查询当前登录的会员的地址信息
            Long id = memberVo.getId();
            List<MemberAddressVo> addressVoList = memberFeignService.getAddress(id);
            confirmVo.setAddress(addressVoList);
        }, poolExecutor);

        CompletableFuture<Void> future2 = CompletableFuture.runAsync(() -> {
            //同步主线程的requestAttributes
            RequestContextHolder.setRequestAttributes(requestAttributes);

            //2、查询购物车中，选中的商品信息
            List<OrderItemVo> userCartItems = cartFeignService.getUserCartItems();
            confirmVo.setItems(userCartItems);
        }, poolExecutor);

        try {
            CompletableFuture.allOf(future1, future2).get();
        } catch (Exception e) {
            e.printStackTrace();
        }
        //3、计算订单的总金额和需要支付的总金额————在VO中自己计算

        //4、生成放重复提交的token
        String uuidToken = UUID.randomUUID().toString().replaceAll("-", "");
        //5、保存到redis中  order:token：用户编号
        redisTemplate.opsForValue().set(OrderConstant.ORDER_TOKEN_PREFIX + memberVo.getId(), uuidToken);
        confirmVo.setOrderToken(uuidToken);

        return confirmVo;
    }

    /**
     * seata分布式事务管理，需要通过@GlobalTransactional注解修饰
     * 结算确认页——提交订单
     */
    @GlobalTransactional
    @Transactional
    @Override
    public OrderResponseVo orderSubmit(OrderSubmitVo vo) {
        //需要返回响应的对象
        OrderResponseVo responseVo = new OrderResponseVo();
        //1、获取当前登录的用户信息
        MemberVo memberVo = (MemberVo) AuthInterceptor.threadLocal.get();
        //验证是否重复提交。 保证redis中的token 的查询和删除是一个原则性的操作
        String key = OrderConstant.ORDER_TOKEN_PREFIX + memberVo.getId();
        String scripts = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";

        //影响的行数
        Long result = (Long) redisTemplate.execute(new DefaultRedisScript<Long>(scripts, Long.class), Arrays.asList(key), vo.getOrderToken());

        if (result == 0) {
            //表示验证失败 ，说明是重复提交
            responseVo.setCode(1);
            return responseVo;
        }

        //2、创建订单与订单项信息
        OrderCreateDTO orderCreateDTO = createOrder(vo);
        responseVo.setOrderEntity(orderCreateDTO.getOrderEntity());

        //3、保存订单信息
        saveOrder(orderCreateDTO);

        //4、锁定库存信息  要传递的数据：订单号、skuId、skuName、商品的数量
        lockWareSkuStock(responseVo, orderCreateDTO);

        // int a = 1/0;//执行异常，应该会回滚

        //订单成功后，需要给消息中间件 发送延迟30分钟的关单消息
        orderMsgProducer.sendOrderMsg(orderCreateDTO.getOrderEntity().getOrderSn());

        return responseVo;
    }

    /**
     * 根据订单编号，查询出相关的订单信息，封装到PayVo中
     *
     * @param orderSn 订单编号
     */
    @Override
    public PayVo getOrderPay(String orderSn) {
        //根据订单编号，查询出相关的订单信息
        OrderEntity orderEntity = this.getBaseMapper().getOrderByOrderSn(orderSn);
        //通过订单信息，封装PayVo对象
        PayVo payVo = new PayVo();
        payVo.setOut_trade_no(orderSn);
        //设置订单总金额，保留2位小数 向上取整
        payVo.setTotal_amount(orderEntity.getTotalAmount().setScale(2, RoundingMode.UP).toString());
        //订单名称和描述——创建订单的时候没有设置，先全部设置OrderSn
        payVo.setSubject(orderEntity.getOrderSn());
        payVo.setBody(orderEntity.getOrderSn());

        return payVo;
    }

    /**
     * 支付完成后，后面处理的操作.
     * 例如：更新订单状态、更新库存信息、购物车中移除已经支付的商品....等
     * 也是要用到分布式事务
     *
     * @param orderSn 订单编号
     */
    @Override
    public void handleOrderComplete(String orderSn) {
        //1、更新订单状态
        this.getBaseMapper().updateOrderStatus(orderSn, OrderConstant.OrderStatusEnum.TO_SEND_GOODS.getCode());
        //2、更新库存信息（库存信息递减）

        //3、购物车中移除已经支付的商品（拼接到存在redis中的key，从redis中删除）

        //4、更新会员积分。。。等

    }

    /**
     * 锁定库存信息
     */
    private void lockWareSkuStock(OrderResponseVo responseVo, OrderCreateDTO orderCreateDTO) {
        WareSkuLockVo wareSkuLockVo = new WareSkuLockVo();
        wareSkuLockVo.setOrderSn(orderCreateDTO.getOrderEntity().getOrderSn());
        List<OrderItemVo> orderItemVos = orderCreateDTO.getOrderItemEntity().stream().map(item -> {
            OrderItemVo itemVo = new OrderItemVo();
            itemVo.setSkuId(item.getSkuId());
            itemVo.setTitle(item.getSkuName());
            itemVo.setCount(item.getSkuQuantity());//购买的数量
            return itemVo;
        }).collect(Collectors.toList());
        wareSkuLockVo.setItemVos(orderItemVos);
        //远程远程仓库服务，锁定库存方法
        R r = wareFeignService.orderLockStock(wareSkuLockVo);
        if (r.getCode() == 0) {
            //说明锁定库存成功
            responseVo.setCode(0);//表示创建订单成功
        } else {
            //说明锁定库存失败
            responseVo.setCode(2);//表示库存不足，锁定失败
        }
    }

    /**
     * 生成订单数据的方法
     */
    private void saveOrder(OrderCreateDTO orderCreateDTO) {
        //1、订单数据
        OrderEntity orderEntity = orderCreateDTO.getOrderEntity();
        orderService.save(orderEntity);

        //2、订单项数据
        List<OrderItemEntity> orderItemEntity = orderCreateDTO.getOrderItemEntity();
        orderItemService.saveBatch(orderItemEntity);
    }

    /**
     * 创建订单的方法
     *
     * @param vo 提交的信息vo对象
     */
    private OrderCreateDTO createOrder(OrderSubmitVo vo) {
        OrderCreateDTO createDTO = new OrderCreateDTO();
        //1、创建订单
        OrderEntity orderEntity = buildOrder(vo);
        createDTO.setOrderEntity(orderEntity);

        //2、创建订单项 OrderItemEntity
        List<OrderItemEntity> orderItemEntitys = buildOrderItems(orderEntity.getOrderSn());
        createDTO.setOrderItemEntity(orderItemEntitys);

        //根据订单项，计算出订单总额
        BigDecimal totalAmount = new BigDecimal(0);
        for (OrderItemEntity orderItemEntity : orderItemEntitys) {
            //订单项的每个sku的价格 * 数量
            totalAmount = totalAmount.add(orderItemEntity.getSkuPrice().multiply(new BigDecimal(orderItemEntity.getSkuQuantity())));
        }
        orderEntity.setTotalAmount(totalAmount);

        createDTO.setOrderItemEntity(orderItemEntitys);

        return createDTO;
    }

    /**
     * 通过购物车中选中的商品，来创建对应的购物项信息
     *
     * @param orderSn 订单编号
     */
    private List<OrderItemEntity> buildOrderItems(String orderSn) {
        List<OrderItemEntity> orderItemEntityList = new ArrayList<>();
        // 获取购物车中的商品信息 选中的
        List<OrderItemVo> userCartItems = cartFeignService.getUserCartItems();
        if (userCartItems != null && userCartItems.size() > 0) {
            // 统一根据SKUID查询出对应的SPU的信息
            List<Long> spuIds = new ArrayList<>();
            for (OrderItemVo orderItemVo : userCartItems) {
                if (!spuIds.contains(orderItemVo.getSpuId())) {
                    spuIds.add(orderItemVo.getSpuId());
                }
            }
            // 远程调用商品服务获取到对应的SPU信息
            Long[] spuIdsArray = new Long[spuIds.size()];
            List<OrderItemSpuInfoVO> spuInfos = productFeignService.getOrderItemSpuInfoBySpuId(spuIds.toArray(spuIdsArray));
            //转成map集合
            Map<Long, OrderItemSpuInfoVO> map = spuInfos.stream().collect(Collectors.toMap(OrderItemSpuInfoVO::getId, item -> item));

            for (OrderItemVo userCartItem : userCartItems) {
                // 获取到商品信息对应的 SPU信息
                OrderItemSpuInfoVO spuInfo = map.get(userCartItem.getSpuId());
                //构建购物车对象
                OrderItemEntity orderItemEntity = buildOrderItem(userCartItem, spuInfo);
                //绑定订单编号
                orderItemEntity.setOrderSn(orderSn);
                //添加每个订单项
                orderItemEntityList.add(orderItemEntity);
            }
        }

        return orderItemEntityList;
    }

    /**
     * 根据1个购物车中的商品，创建对应的订单项
     *
     * @param userCartItem 订单项信息
     * @param spuInfo      spu信息
     * @return
     */
    private OrderItemEntity buildOrderItem(OrderItemVo userCartItem, OrderItemSpuInfoVO spuInfo) {
        OrderItemEntity entity = new OrderItemEntity();
        //sku信息
        entity.setSkuId(userCartItem.getSkuId());
        entity.setSkuName(userCartItem.getTitle());
        entity.setSkuPic(userCartItem.getImage());
        entity.setSkuPrice(userCartItem.getPrice());//价格
        entity.setSkuQuantity(userCartItem.getCount());
        //销售属性
        List<String> skuAttrList = userCartItem.getSkuAttr();
        String skuAttr = StringUtils.collectionToDelimitedString(skuAttrList, ";");
        entity.setSkuAttrsVals(skuAttr);
        //spu信息
        entity.setSpuId(spuInfo.getId());
        entity.setCategoryId(spuInfo.getCatalogId());
        entity.setSpuName(spuInfo.getSpuName());
        entity.setSpuBrand(spuInfo.getBrandName());
        entity.setSpuPic(spuInfo.getImg());

        //优惠信息  忽略
        //积分信息
        entity.setGiftGrowth(userCartItem.getPrice().intValue());
        entity.setGiftIntegration(userCartItem.getPrice().intValue());
        return entity;
    }

    /**
     * 创建订单
     */
    private OrderEntity buildOrder(OrderSubmitVo vo) {
        //1、创建OrderEntity
        OrderEntity orderEntity = new OrderEntity();
        //创建订单编号（数据库字段长度是32位，已经修改为64位）
        String orderSn = IdWorker.getTimeId();
        orderEntity.setOrderSn(orderSn);
        orderEntity.setPayAmount(vo.getPayAmount());//应付金额
        //设置会员相关信息
        MemberVo memberVo = (MemberVo) AuthInterceptor.threadLocal.get();
        orderEntity.setMemberId(memberVo.getId());
        orderEntity.setMemberUsername(memberVo.getUsername());
        //根据收货地址id，获取收货地址信息，并设置
        MemberAddressVo memberAddressVo = memberFeignService.getAddressById(vo.getAddrId());
        orderEntity.setReceiverCity(memberAddressVo.getCity());
        orderEntity.setReceiverDetailAddress(memberAddressVo.getDetailAddress());
        orderEntity.setReceiverName(memberAddressVo.getName());
        orderEntity.setReceiverPhone(memberAddressVo.getPhone());
        orderEntity.setReceiverPostCode(memberAddressVo.getPostCode());
        orderEntity.setReceiverRegion(memberAddressVo.getRegion());
        orderEntity.setReceiverProvince(memberAddressVo.getProvince());

        //设置订单状态  待支付
        orderEntity.setStatus(OrderConstant.OrderStatusEnum.FOR_THE_PAYMENT.getCode());
        return orderEntity;
    }
}
